-- Shadow2D

--[[
作者: @破晓_年华        
原帖: http://tieba.baidu.com/p/4542665919
--]]

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    Pos=vec2(WIDTH,200)
    Light=Shadow2D(Pos)
    Light2=Shadow2D(Pos)
    Lits={}
    Ball=physics.body(CIRCLE,100)
    Ball.x=WIDTH/2
    Ball.y=HEIGHT/2
    Ball.type=STATIC
    fill(255, 255, 255, 255)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0, 231, 255, 255)
    Light.lightPos=vec2(CurrentTouch.x,CurrentTouch.y)
    Light:draw()
    Light:drawLight(Light.lightPos)
    --[[Light2:draw()
    Light2:drawLight(Light2.lightPos)]]
    -- This sets the line thickness
    strokeWidth(5)
    drawBody(Ball)
    -- Do your drawing here
    
end

function drawBody(body)
    -- Push style and transform matrix so we can restore them after
    pushStyle()
    pushMatrix()
    
    strokeWidth(5)
    stroke(148, 224, 135, 255)
    translate(body.x, body.y)
    rotate(body.angle)
    
    -- Draw body based on shape type
    if body.shapeType == POLYGON then
        strokeWidth(3.0)
        local points = body.points
        for j = 1,#points do
            a = points[j]
            b = points[(j % #points)+1]
            line(a.x, a.y, b.x, b.y)
        end
    elseif body.shapeType == CHAIN or body.shapeType == EDGE then
        strokeWidth(3.0)
        local points = body.points
        for j = 1,#points-1 do
            a = points[j]
            b = points[j+1]
            line(a.x, a.y, b.x, b.y)
        end
    elseif body.shapeType == CIRCLE then
        strokeWidth(3.0)
        line(0,0,body.radius-3,0)
        ellipse(0,0,body.radius*2)
    end
    
    -- Restore style and transform
    popMatrix()
    popStyle()
end


Shadow2D = class()

function Shadow2D:init(la)
    self.lightPos = la
    self.m = mesh()
    self.lm = mesh()
    self.m.texture="Documents:Moon"
end

function Shadow2D:draw()
    --self:drawLight(self.lightPos)
    strokeWidth(2)
    self:merchRay()   
    for i = 1,#self.ray,2 do
        local shadowPos1 = self.ray[i]
        local shadowPos2 = self.ray[i + 1]
        local pos1 = (shadowPos1 - self.lightPos):normalize() * 1000 + shadowPos1
        local pos2 = (shadowPos2 - self.lightPos):normalize() * 1000 + shadowPos2
        
        self.m.vertices = {
        pos1,shadowPos1,shadowPos2,shadowPos2,pos2,pos1
        }
        local c1,c2 = color(0, 0, 0, 103) ,color(0, 0, 0, 93)
        self.m.colors = {
        c2,c1,c1,c1,c2,c2
        }
        self.m:draw()
    end 
end

function Shadow2D:drawLight(lightpos)
    self.lm.vertices = {vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT),vec2(0,0)}
    self.lm.shader = shader("Documents:light")
    self.lm.shader.Size = 4
    self.lm.shader.Shine = 0.45
    self.lm.shader.Pos = lightpos
    self.lm:draw()
end

function Shadow2D:merchRay()  
    self.ray = {}
    local step = 180
    local state
    local exchange = false
    local last = nil
    local lastBody = nil
    for i = 0,step * math.pi * 2 do
        local sa = vec2(math.sin(i / step),math.cos(i / step)) * 2000 + self.lightPos
        local ag = physics.raycast(self.lightPos,sa)
        if ag then
            if not state then
                exchange = true
                state = 1
            end
            if lastBody and ag.body ~= lastBody then         
                local r1 = last - self.lightPos
                local r2 = ag.point - self.lightPos
                r1,r2 = self:findRayB(r1,r2,10,lastBody,3)
                table.insert(self.ray,r1 + self.lightPos)
                table.insert(self.ray,r2 + self.lightPos)
            end
            if state == 0 then
                local r1 = last - self.lightPos
                local r2 = ag.point - self.lightPos
                table.insert(self.ray,self:findRay(r1,r2,5,state,2) + self.lightPos)
                state = 1
            end
            last = ag.point
            lastBody = ag.body
        else
            lastBody = nil
            if not state then
                state = 0
            end
            if state == 1 then
                local r1 = last - self.lightPos
                local r2 = sa - self.lightPos
                table.insert(self.ray,self:findRay(r1,r2,5,state,2) + self.lightPos)
                state = 0
            end
            last = sa
        end
    end
    if #self.ray % 2 ~= 0 then
        table.remove(self.ray,1)
    end
    if exchange then
        table.insert(self.ray,1,table.remove(self.ray,#self.ray))
    end
end

function Shadow2D:findRay(r1,r2,step,state,depth)
    if depth <= 0 then
        if state == 0 then
            return r2
        elseif state == 1 then
            return r1
        end
        return r2
    end
    local t,ray,ag,last,sa
    last = r1
    for i = 0,step do
        t = i / step
        ray = (1 - t) * r1 + t * r2
        sa = self.lightPos + ray * 10000
        ag = physics.raycast(self.lightPos,sa)
        if ag then
            if state == 0 then
                local ra1 = last - self.lightPos
                local ra2 = ag.point - self.lightPos
                return self:findRay(ra1,ra2,step,state,depth - 1)
            end
            last = ag.point
        else
            if state == 1 then
                local ra1 = last - self.lightPos
                local ra2 = sa - self.lightPos
                return self:findRay(ra1,ra2,step,state,depth - 1)
            end
            last = sa
        end
    end
    return r2
end

function Shadow2D:findRayB(r1,r2,step,lastBody,depth)
    if depth <= 0 then
        return r1,r2
    end
    local t,ray,ag,last,sa
    last = r1
    for i = 0,step do
        t = i / step
        ray = (1 - t) * r1 + t * r2
        sa = self.lightPos + ray * 10000
        ag = physics.raycast(self.lightPos,sa)
        if ag then
            if ag.body ~= lastBody then
                local ra1 = last - self.lightPos
                local ra2 = ag.point - self.lightPos
                return self:findRayB(ra1,ra2,step,lastBody,depth - 1)
            end
            last = ag.point
        end
    end
    return r1,r2
end


